#include "launch/launch_parser.h"
#include "logger.h"
#include "utils/utils.h"
#include "launch/node_parser.h"
#include "launch/param_parser.h"
#include "launch/env_parser.h"
#include "launch/arg_parser.h"
#include "launch/include_parser.h"
#include <filesystem>

LaunchParser::LaunchParser()
{}
LaunchParser::~LaunchParser()
{

}
std::optional<LaunchNode> LaunchParser::parserLaunchFile(const std::string &launchFile)
{
    std::filesystem::path launchPath(launchFile);
    if (!std::filesystem::exists(launchPath))
    {
        ROS_ERROR("launch file is not exist, %s", launchFile.c_str());
        return std::nullopt;
    }

    tinyxml2::XMLDocument document;
    tinyxml2::XMLError xmlError = document.LoadFile(launchFile.c_str());
    if (tinyxml2::XMLError::XML_SUCCESS != xmlError)
    {
        ROS_ERROR("launch format error, %s", document.ErrorStr());
        return std::nullopt;
    }
    return parserDocument(document);
}
std::optional<LaunchNode> LaunchParser::parserDocument(const tinyxml2::XMLDocument &document)
{
    const tinyxml2::XMLElement *launchElement = document.FirstChildElement();
    if (nullptr == launchElement)
    {
        ROS_ERROR("root first element is not null");
        return std::nullopt;
    }
    const char *launchName = launchElement->Name();
    if (nullptr == launchName || 0 != strncmp(launchName, "launch", strlen("launch")))
    {
        ROS_ERROR("root first element name is not null or is not launch");
        return std::nullopt;
    }
    LaunchNode launchNode;
    //launch node内部
    const tinyxml2::XMLElement *element = launchElement->FirstChildElement();
    do
    {
        if (nullptr == element)//遍历完
        {
            break;
        }
        if (!parserLaunchElement(element, launchNode))
        {
            break;
        }
        element = element->NextSiblingElement();
    }
    while (true);

    return launchNode;
}
bool LaunchParser::parserLaunchElement(const tinyxml2::XMLElement *element, LaunchNode &launchNode)
{
    const char *name = element->Name();
    if (nullptr == name)
    {
        ROS_ERROR("launch node element name is nullptr");
        return false;
    }
    bool result = true;
    if (strncmp(name, "node", strlen("node")) == 0)
    {
        NodeParser nodeParser;
        if (auto node = nodeParser.parser(element))
        {
            launchNode.addLaunchNode(node.value());
            result = true;
        }
        else
        {
            result = false;
        }
    }
    else if (strncmp(name, "include", strlen("include")) == 0)
    {
        IncludeParser includeParser;
        if (auto node = includeParser.parser(element))
        {
            launchNode.addLaunchNode(node.value());
            result = true;
        }
        else
        {
            result = false;
        }
    }
    else if (strncmp(name, "param", strlen("param")) == 0)
    {
        ParamParser paramParser("/");
        result = paramParser.parser(element);
    }
    else if (strncmp(name, "arg", strlen("arg")) == 0)
    {
        ArgParser argParser;
        result = argParser.parser(element);
    }
    else if (strncmp(name, "env", strlen("env")) == 0)
    {
        EnvParser envParser;
        result = envParser.parser(element);
    }
    else
    {
        ROS_ERROR("unknown launch node type, name: %s, xml: %s", name, Utils::formatElement(element).c_str());
    }
    return result;
}
